﻿#region copyright

/// JavaProperties.cs
/// Copyright (c) 2009, BYU ISRL (http://isrl.cs.byu.edu/).
/// All rights reserved.
/// 
/// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the 
/// following conditions are met:
/// 
///    * Redistributions of source code must retain the above copyright notice, this list of conditions and the 
///      following disclaimer.
///    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
///      following disclaimer in the documentation and/or other materials provided with the distribution.
///    * Neither the name of Brigham Young University, the Internet Security Research Lab, nor the names of their 
///      contributors may be used to endorse or promote products derived from this software without specific prior 
///      written permission.
///      
/// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
/// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
/// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
/// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
/// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
/// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 
/// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#endregion

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;

namespace BYU.ISRL.Utils
{
	public class JavaProperties : Dictionary<string, string>
	{
		private static string DEFAULT_ENCODING = "UTF-8";
		private static string DTD_URL = "http://java.sun.com/dtd/properties.dtd";

		private static string XML_ROOT_ELEMENT_NAME = "properties";
		private static string XML_COMMENT_ELEMENT_NAME = "comment";
		private static string XML_ENTRY_ELEMENT_NAME = "entry";
		private static string XML_ENTRY_KEY_ATTR_NAME = "key";

		public JavaProperties()
			: base()
		{
			return;
		}

		public void Load(Stream input)
		{
			Load(new StreamReader(input));
		}

		public void Load(TextReader reader)
		{
			string line, trimmed;
			string[] split;

			while ((line = reader.ReadLine()) != null)
			{
				trimmed = line.Trim();
				// if line begins with '#' or '!', it's a comment
				if (trimmed.Length > 0)
				{
					if (trimmed.Substring(0) != "#" && trimmed.Substring(0) != "!")
					{
						char[] delim = new char[1];
						delim[0] = '=';
						split = trimmed.Split(delim);
						if (split.Length >= 2)
							this.Add(split[0], split[1]);
					}
				}
			}
		}

		public void LoadFromXml(Stream input)
		{
			// load data
			XmlReaderSettings settings = new XmlReaderSettings();
			settings.ProhibitDtd = false;
			XmlReader reader = XmlReader.Create(input, settings);

			XmlDocument doc = new XmlDocument();
			doc.Load(reader);

			// parse it
			XmlElement root = doc.DocumentElement;

			// populate dictionary
			foreach (XmlNode node in root.ChildNodes)
			{
				XmlElement elem = node as XmlElement;

				if (elem != null)
				{
					if (elem.Name == XML_ENTRY_ELEMENT_NAME)
					{
						this.Add(elem.Attributes[XML_ENTRY_KEY_ATTR_NAME].Value, elem.InnerText);
					}
				}
			}
		}

		public void SetProperty(string key, string value)
		{
			this.Add(key, value);
		}

		public void Store(TextWriter writer, string comments)
		{
			// write comments
			if (comments != null)
				writer.WriteLine("#" + comments);

			// write date comment
			// NOTE: To fully comply with the Java implementation, the time zone should be "GMT", "PST", etc., not "-06:00" or whatever.
			writer.WriteLine("#" + DateTime.Now.ToString("ddd MMM dd HH:mm:ss K yyyy"));

			foreach (string key in this.Keys)
			{
				writer.WriteLine(key + "=" + this[key]);
			}

			writer.Close();
		}

		public void Store(Stream outputStream, string comments)
		{
			Store(new StreamWriter(outputStream), comments);
		}

		public void StoreToXml(Stream outputStream, string comments)
		{
			StoreToXml(new StreamWriter(outputStream), comments, DEFAULT_ENCODING);
		}

		public void StoreToXml(TextWriter writer, string comments, string encoding)
		{
			// prepare XML writer
			XmlWriterSettings settings = new XmlWriterSettings();
			settings.Indent = true;
			settings.CloseOutput = true;
			settings.Encoding = Encoding.GetEncoding(encoding);

			XmlWriter xmlWriter = XmlWriter.Create(writer, settings);

			// begin writing contents

			xmlWriter.WriteDocType(XML_ROOT_ELEMENT_NAME, null, DTD_URL, null);

			xmlWriter.WriteStartElement(XML_ROOT_ELEMENT_NAME);

			// write comments, if any
			if (comments != null)
			{
				xmlWriter.WriteStartElement(XML_COMMENT_ELEMENT_NAME);
				xmlWriter.WriteString(comments);
				xmlWriter.WriteEndElement();
			}

			// write the key/value pairs
			foreach (string key in this.Keys)
			{
				xmlWriter.WriteStartElement(XML_ENTRY_ELEMENT_NAME);
				xmlWriter.WriteAttributeString(XML_ENTRY_KEY_ATTR_NAME, key);
				xmlWriter.WriteString(this[key]);
				xmlWriter.WriteEndElement();
			}

			xmlWriter.WriteEndElement();

			// close it up
			xmlWriter.Close();
		}

		public HashSet<string> PropertyNames
		{
			get
			{
				return new HashSet<string>(this.Keys);
			}
		}
	}
}
